package com.norriekoelle.virtualsensors.kinect.Connection;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;

import android.text.format.DateFormat;
import android.util.Log;
import android.widget.ImageView;

import com.norriekoelle.virtualsensors.kinect.R;
import com.norriekoelle.virtualsensors.kinect.ConnectionManager;
import com.norriekoelle.virtualsensors.kinect.hotspots.ClientActivity;

/**
 * Create a client connection
 * 
 * Note: Internet permissions in manifest
 * 
 * @author norrielm
 *
 */
public class WifiClient {//implements Runnable{
	
	//Socket vars
	private Socket echoSocket = null;
	private DatagramSocket udpSocket;
    private DataInputStream is;
	private DataOutputStream os;
	//Socket state
	public boolean wifiAvailable = true;
    public boolean serverConnected=false;
    private boolean connected = false;
	public boolean exiting = false;
	//Connection thread
	//Android 
	private ClientActivity context;
	private ConnectionManager cm;
	private int userID = -1;
	private String serverAddr = "10.0.1.55";  
	private int port = 55555;
	private final String ID = "HTC Hero";
	//Application
	//String lastHotspot ="";
	protected boolean reconnecting = false;
	
	
    public WifiClient(ClientActivity c, ConnectionManager m){
    	context = c;
    	cm = m;
    	
    	try {
    		getLocalIpAddress();    		
    		connectToServer();    	
    	}
    	catch(Exception e){
        	wifiAvailable = false;
    	}        
    }
    
    /** 
     * Get the local ip address
     * 
     * @return
     */
    private String getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) { return inetAddress.getHostAddress().toString(); }
                }
            }
        } catch (SocketException ex) {
            Log.e("AndroidClient", ex.toString());
        }
        return null;
    }   
    
    public void connectToServer() throws UnknownHostException, IOException{
        message("Starting connection threads...");
		connected = false; 
		reconnecting=false;
		exiting=false;
		serverConnected=false;
		 
        new Thread (new Runnable(){
			@Override
			public void run() {
				//busy wait while not connected: should use wait/notify 
				while(!connected){
					try{Thread.sleep(500);}catch(InterruptedException e){}
				}			
				//receive messages
				while(!exiting || !reconnecting){
					receiveUDPMessage();

					try{Thread.sleep(20);}catch(InterruptedException e){}
				}
			}        	
        }).start();
        
        /**
         * SOMETHING IN ANDROID CLIENTS THAT AREN'T ACKING I DON'T THINK...
         */
        
        /**
         * Set up connection
         */
        new Thread(new Runnable(){
			@Override
			public void run() { 
		
				//Create a connectionless UDP socket	
				Log.d("AndroidClient", "starting udp conn" );
				while(!connected && !exiting){
			    	try {
						udpSocket = new DatagramSocket();
						connected = true;
						message("Socket created");
					} catch (SocketException e) {
						Log.d("AndroidClient", "socket ex " + e.getMessage());
						e.printStackTrace();
					}
				}
				
				context.runOnUiThread(new Runnable(){
					@Override
					public void run() {
						context.message("connecting...");
					}
		    	});
				
				//prompt server to send a message
				while(!exiting && !serverConnected){
					sendUDPMessage("", "connect\n");
					try{Thread.sleep(4000);}catch(InterruptedException e){}
				}
				Log.d("AndroidClient", "Connection established." );
			}        	
        }).start();
    }
    
    public void connected(){
    	serverConnected=true;
    	context.runOnUiThread(new Runnable(){
			@Override
			public void run() {
				context.message("connected.");		
			}
    	});
    }
    
    public void reconnect(){
    	reconnecting = true;
		udpSocket.close();
		
    	context.runOnUiThread(new Runnable(){
			@Override
			public void run() {
				ImageView logo = (ImageView) context.findViewById(R.id.logo);
		    	logo.setImageDrawable(context.getResources().getDrawable(R.drawable.logo_disconnected));
		    	context.message("reconnecting...");
			}
    	});
    	
    	try {
			this.connectToServer();
		} catch (UnknownHostException e) {
			context.message("reconnect failed");
		} catch (IOException e) {
			context.message("reconnect failed");
		}
    }
    
	public void closeServer() {
		message("closing server");
		sendUDPMessage("close", "closing");
		
		try {
			//clientAlive = false;
			if(os!=null)
				os.close();			
			if(is!=null)
				is.close();
			if(echoSocket!=null)
				echoSocket.close();
			if(udpSocket!=null)
				udpSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*@Override
	public void run() {
		connected = false;  
		 
		//start UDP				
		boolean conn = false;
		Log.d("AndroidClient", "starting udp conn" );
		while(!conn && !exiting){
	    	//message("Connecting to " + serverAddr + ":"+port);
	    	try {	
				udpSocket = new DatagramSocket();
				conn = true;
				message("Socket created");
			} catch (SocketException e) {
				Log.d("AndroidClient", "socket ex " + e.getMessage());
				e.printStackTrace();
			}			
		}
		//conn
		while(!exiting && !serverConnected){
			sendUDPMessage("", "connect\n");
			try{
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		Log.d("AndroidClient", "starting loop" );
		while(!exiting){
			receiveUDPMessage();
			try{
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	     message("exiting...");
	}*/
	
	/**
	 * Send a message into the UDP socket 
	 * 
	 * @param command
	 * @param message
	 */
	public void sendUDPMessage(String command, String message){
		if(udpSocket!=null){
			//tell everyone we're sending a message
			message("sending "+message);
			message= String.format("%s %s %s\n", command, ID, message).trim();
			Log.d("AndroidClient", "sending : " + message );
			
			byte[] buf = new byte[1024];
			buf = message.getBytes();
			DatagramPacket packet;
			//send
			try {
				packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(serverAddr), port);
				udpSocket.send(packet);
			} catch (UnknownHostException e1) {
				Log.d("AndroidClient", "unknown host");
			}catch (IOException e) {
				Log.d("AndroidClient", "io ex");
		}
		}
	}
	
	/**
	 * Experimenting with how much Android buffers up before sending...
	 * 
	 * @param length
	 * @param s
	 * @return
	 *
	public String longerString(int length, String s){
		for(int i=s.length(); i<length; i++){
			s+=" ";
		}
		return s;
	}*/
	
	/**
	 * Blocking receive from the UDP socket
	 */
	public void receiveUDPMessage(){
		if(udpSocket!=null && !udpSocket.isClosed()){
			Log.d("AndroidClient", "receive..." );
			byte[] buf = new byte[30];
			DatagramPacket packet = new DatagramPacket(buf, buf.length);	        
	        try {
				udpSocket.receive(packet);
			} catch (IOException e) {
				Log.d("UDP", "S: err...");
				e.printStackTrace();
			}
	        
	        String serverMessage = new String(packet.getData()).toLowerCase();
	        String time = "(" + DateFormat.format("hh:mm:ss", new Date()) + ")"; 
			Log.d("AndroidClient", time + " received " + serverMessage );
		
			//deal with message
			cm.handleMessage(serverMessage);
		}
	}
	
	/**
	 * Log a message
	 * @param message
	 */
    public void message(String message){
    	Log.d("AndroidClient",message);
    }
	
}
